package broker

import (
	"context"
	"encoding/json"
	"fmt"
	"gitlab.local/backend/proto/dto"
	"manager/bll"
	log "manager/collector/logger"
	"manager/event"
	"manager/lib/infra"
	"manager/lib/trace_log"
	"manager/model"
	"manager/model/entity"
	"manager/model/po"
	"manager/store/postgres"
	"manager/store/redis"
	"manager/utils"
	"manager/utils/goroutine_pool"
	"math"
	"sync"
	"time"
)

const (
	// DevicePowerOnStatusKey 设备状态
	DevicePowerOnStatusKey = "device_status:device_power"
	// DeviceLocationKey 骑行记录
	DeviceLocationKey = "device_location"
)

type Service interface {
	onEvent(ed *event.Data)
	init() func()
}

var DeviceCyclingReport = NewDeviceCyclingReport()

// DeviceCyclingReport 设备骑行数据上报
type deviceCyclingReport struct {
	timerSyncMap sync.Map
}

func NewDeviceCyclingReport() *deviceCyclingReport {
	obj := &deviceCyclingReport{}
	obj.timerSyncMap = sync.Map{}
	//obj.timerSyncMap.Store(int64(168), struct{}{})
	go obj.onPolling()
	return obj
}

func (d *deviceCyclingReport) init() func() {
	return func() {}
}

func (d *deviceCyclingReport) onEvent(data *event.Data) {
	if data == nil || (data.Type != event.Event_Power_On_Finish && data.Type != event.Event_Power_Off_Finish) {
		return
	}

	log.Info("---------------- deviceCyclingReport onEvent data:", utils.ToJsonString(data))

	// 上电
	if data.Type == event.Event_Power_On_Finish {
		// 解析参数
		obj := data.Data.(*event.BikePowerOn)
		if obj == nil {
			log.Error("deviceCyclingReport onEvent err")
			return
		}

		// 获取骑行用户
		isOn, userId := DeviceStatus.GetPowerOnUser(obj.EbikeId)
		if isOn == false || userId == 0 {
			return
		}

		// 车辆上电
		bikeInfo, _ := bll.CacheBike.GetBikeInfoById(obj.EbikeId)
		if bikeInfo != nil && bikeInfo.SNCode != "" {
			trace_log.DebugLogger(bikeInfo.SNCode, "Event_Power_On_Finish onEvent", "车辆上电")
		}

		// 从数据库中查询
		/*		_, routeList, _ := postgres.RouteList.List(&model.RouteListListRequest{
					Index:   1,
					Size:    1,
					EbikeId: obj.EbikeId,
				}, true)

				if len(routeList) > 0 && routeList[0].IsFinish == 0 {
					// 更新骑行记录,上次没有同步的
					d.UpdateCyclingRecode(obj.EbikeId, true)
				}
		*/
		// 下电
		if bSuccess := redis.KV.DeletedRedisItem(context.Background(), redis.CacheList, fmt.Sprintf("%v:%v", DeviceLocationKey, obj.EbikeId)); !bSuccess {
			log.Errorf("deviceCyclingReport DeletedRedisItem failed")
		}

		// 创建骑行记录
		bll.RouteList.Create(context.Background(), &model.RouteListCreateRequest{
			EbikeId: obj.EbikeId,
			UserId:  userId,
		})

		// 定时同步骑行记录
		if _, ok := d.timerSyncMap.Load(obj.EbikeId); !ok {
			d.timerSyncMap.Store(obj.EbikeId, struct{}{})
		}

		if bikeInfo != nil && bikeInfo.SNCode != "" {
			trace_log.DebugLogger(bikeInfo.SNCode, "------- deviceCyclingReport Event_Power_On_Finish 车辆上电完成")
		}

		return
	}

	// 下电
	if data.Type == event.Event_Power_Off_Finish {
		// 解析参数
		obj := data.Data.(*event.BikePowerOff)
		if obj == nil {
			log.Error("deviceCyclingReport onEvent err")
			return
		}

		defer func() {
			// 下电
			key := fmt.Sprintf("%v", obj.EbikeId)
			if err := redis.KV.DelValue(context.Background(), DevicePowerOnStatusKey, key); err != nil {
				log.Errorf("deviceStatus AddValueWithExpireTime err:%v", err)
			}
		}()

		// 车辆下电
		bikeInfo, _ := bll.CacheBike.GetBikeInfoById(obj.EbikeId)
		if bikeInfo != nil && bikeInfo.SNCode != "" {
			trace_log.DebugLogger(bikeInfo.SNCode, "Event_Power_Off_Finish onEvent", "下电")
		}

		// 加分布式锁
		conn := redis.KV.GetRedisPool().Get()
		lock := &redis.TRedLock{}
		bSuccess, _ := lock.Lock(conn, fmt.Sprintf("poweroff_%v", obj.EbikeId), 10)
		if !bSuccess {
			log.Errorf("deviceCyclingReport lock fail")
			trace_log.DebugLogger(bikeInfo.SNCode, "deviceCyclingReport lock fail")
			return
		}
		defer lock.UnLock(conn)

		// 用户ID
		_, userId := DeviceStatus.GetPowerOnUser(obj.EbikeId)
		if userId == 0 {
			trace_log.DebugLogger(bikeInfo.SNCode, "Event_Power_Off_Finish onEvent", "下电", " err userid is 0")
			return
		}

		// 结束骑行轨迹
		trace_log.DebugLogger(bikeInfo.SNCode, "Finishing recoding bike:%d routes", obj.EbikeId)
		err := d.UpdateCyclingRecord(obj.EbikeId, true)
		if err != nil {
			trace_log.DebugLogger(bikeInfo.SNCode, "Event_Power_Off_Finish onEvent", "下电", " UpdateCyclingRecord err:", err)
		}

		if bikeInfo != nil && bikeInfo.SNCode != "" {
			trace_log.DebugLogger(bikeInfo.SNCode, "------- deviceCyclingReport Event_Power_Off_Finish 车辆下电完成")
		}
	}
}

// FinishTrack 结束骑行轨迹
func (d *deviceCyclingReport) FinishTrack(sn string, userId, bikeId int64) {
	// 删除定时同步车辆
	if _, ok := d.timerSyncMap.Load(bikeId); ok {
		d.timerSyncMap.Delete(bikeId)
	}

	// 车辆下电
	trace_log.DebugLogger(sn, "FinishTrack 行车轨迹数据落库")

	// 清理车辆无效骑行轨迹
	goroutine_pool.GetPool().Push(nil, func(data interface{}) error {
		// 清理无效骑行轨迹
		postgres.GetDB(context.Background()).Where("list = ? and user_id = ? and  ebike_id = ? ", "[]", userId, bikeId).Delete(&entity.RouteList{})
		return nil
	})
	return
}

// IsPowerOn 是否上电
func (d *deviceCyclingReport) IsPowerOn(bikeId int64) bool {
	key := fmt.Sprintf("%v", bikeId)
	if !redis.KV.ValueExist(context.Background(), DevicePowerOnStatusKey, key) {
		log.Warnf("bike no power on but report location bikeId:%v", bikeId)
		return false
	}
	return true
}

// UpdateCyclingRecord 保存骑行记录
func (d *deviceCyclingReport) UpdateCyclingRecord(bikeId int64, isEndUpdate bool) error {
	// 加分布式锁
	conn := redis.KV.GetRedisPool().Get()
	lock := &redis.TRedLock{}
	bSuccess, _ := lock.Lock(conn, fmt.Sprintf("update route_%v", bikeId), 10)
	if !bSuccess {
		log.Errorf("deviceCyclingReport lock fail")
		return nil
	}
	defer lock.UnLock(conn)

	defer func() {
		if isEndUpdate {
			// 下电
			if bSuccess := redis.KV.DeletedRedisItem(context.Background(), redis.CacheList, fmt.Sprintf("%v:%v", DeviceLocationKey, bikeId)); !bSuccess {
				log.Errorf("deviceCyclingReport DeletedRedisItem failed")
			}
		}
	}()
	if bikeId == 0 {
		return nil
	}

	bikeInfo, _ := bll.CacheBike.GetBikeInfoById(bikeId)

	// 是否在启动状态
	_, userId := DeviceStatus.GetPowerOnUser(bikeId)
	if userId == 0 {
		// 获取车主进行记录，进行容错
		userId = bll.UserEbike.GetManagerByBikeId(bikeId)
		if userId == 0 {
			trace_log.DebugLogger(bikeInfo.SNCode, "UpdateCyclingRecord userId is 0")
			return fmt.Errorf("UpdateCyclingRecord userId is 0")
		}
	}

	// 数据落库
	trackId, results, _err := Location.PopLocation(bikeId, userId)
	if len(results) == 0 {
		trace_log.DebugLogger(bikeInfo.SNCode, "UpdateCyclingRecord PopLocation len(results) == 0")
		return nil
	}

	if _err != nil && "骑行记录已结束" == _err.Error() {
		return nil
	}

	if trackId == 0 {
		log.Errorf("deviceCyclingReport PopLocation failed bikeId:%v", bikeId)
		trace_log.DebugLogger(bikeInfo.SNCode, "UpdateCyclingRecord PopLocation failed bikeId:", bikeId, " trackId:", trackId, " isEndUpdate:", isEndUpdate)
		return fmt.Errorf("deviceCyclingReport PopLocation failed bikeId:%v", bikeId)
	}

	if bikeInfo != nil {
		trace_log.DebugLogger(bikeInfo.SNCode, "UpdateCyclingRecord 开始更新骑行记录")
	}

	var locationList po.Array[po.GpsLocation]
	var startMileage, endMileage int
	var mileage int
	var startTime, endTime int
	var maxSpeed int
	var avgSpeed int
	for i := 0; i < len(results); i++ {
		lo := results[i]
		// 过滤无效记录
		if lo.Lng == 0 && lo.Lat == 0 {
			continue
		}

		locationList = append(locationList, po.GpsLocation{
			Time:     lo.Time,
			Lat:      float64(results[i].Lat) / 1000000.0,
			Lng:      float64(results[i].Lng) / 1000000.0,
			GpsSpeed: float64(results[i].GpsSpeed) / 100.0,
			Bearing:  float64(results[i].Bearing) / 100.0,
		})

		// 最高速度
		if int(lo.Speed) > maxSpeed {
			maxSpeed = int(lo.Speed)
		}

		// 其实公里数，起始时间
		//if startTime == 0 && lo.Time > 0 && lo.Mileage > 0 {
		//	startTime = int(lo.Time)
		//}
		//if startTime == 0 && lo.Time > 0 && (lo.Mileage > 0 || lo.Speed > 0 || lo.GpsSpeed > 0) {
		if startTime == 0 && lo.Time > 0 {
			// 车动了，开始记录
			startTime = int(lo.Time)
		}

		if startMileage == 0 && lo.Mileage > 0 {
			startMileage = int(lo.Mileage)
		}

		if lo.Mileage > 0 {
			endMileage = int(lo.Mileage)
		}

		//if lo.Time > 0 && lo.Mileage > 0 {
		endTime = int(lo.Time)
		//}
	}

	if len(locationList) == 0 {
		trace_log.DebugLogger(bikeInfo.SNCode, "UpdateCyclingRecord locationList len(locationList) == 0")
		return fmt.Errorf("locationList len(locationList) == 0")
	}

	duration := endTime - startTime
	trace_log.DebugLogger(bikeInfo.SNCode, "start ts:", startTime, ",end ts:", endTime, ",duration:", duration)
	trace_log.DebugLogger(bikeInfo.SNCode, "startTime:", utils.Timestamp2Str(startTime), ",endTime:", utils.Timestamp2Str(endTime), ",duration:", duration)
	// 结束时间判断过滤无效记录
	if endTime-startTime < 10 {
		trace_log.DebugLogger(bikeInfo.SNCode, "UpdateCyclingRecord time is less than 10s")
		return fmt.Errorf("time is less than 10s")
	}

	// 里程
	mileage = endMileage - startMileage
	trace_log.DebugLogger(bikeInfo.SNCode, "startMileage:", startMileage, "endMileage:", endMileage, "mileage:", mileage)
	if mileage < 200 {
		trace_log.DebugLogger(bikeInfo.SNCode, "UpdateCyclingRecord mileage is less than 200m")
		return fmt.Errorf("mileage is less than 200m")
	}

	// 骑行轨迹
	var startName, endName, city string
	var startCoordinate, endCoordinate po.Point

	length := len(locationList)
	if length > 0 {
		info, _ := infra.Lbs.GetGeoInfo(locationList[0].Lng, locationList[0].Lat)
		startName = info.FormattedAddr
		city = info.City
		startCoordinate = po.Point{
			X: locationList[0].Lng,
			Y: locationList[0].Lat,
		}
	}

	if length >= 1 {
		info, _ := infra.Lbs.GetGeoInfo(locationList[length-1].Lng, locationList[length-1].Lat)
		endName = info.FormattedAddr
		endCoordinate = po.Point{
			X: locationList[length-1].Lng,
			Y: locationList[length-1].Lat,
		}
	}

	// 更新轨迹
	if endTime-startTime > 0 {
		//minutes := math.Floor(float64(endTime-startTime) / 60)
		if duration == 0 {
			duration = 1
		}
		//avgSpeed = int(math.Ceil(math.Ceil((float64(mileage)*60)/minutes) / float64(1000)))
		// avg= (mileage/1000)/(duration/3600)=(mileage*3600)/(duration*1000)
		avgSpeed = int(math.Round((float64(mileage) * 3600) / (float64(duration) * 1000)))
		if avgSpeed > 100 {
			avgSpeed = 100
		}
	}

	// 起始点和结束点都是0的时候，不更新轨迹
	if (startCoordinate.X == 0 && startCoordinate.Y == 0) || (endCoordinate.X == 0 && endCoordinate.Y == 0) {
		trace_log.DebugLogger(bikeInfo.SNCode, "UpdateCyclingRecord startCoordinate or endCoordinate is zero")
		return fmt.Errorf("UpdateCyclingRecord startCoordinate or endCoordinate is zero")
	}

	// 过滤点位
	locationList = Location.FilterLocation(locationList)

	isFinish := 0
	if isEndUpdate {
		isFinish = 1
	}
	record := &model.RouteListUpdateRequest{
		Id:              trackId,
		StartName:       &startName,
		StartCoordinate: &startCoordinate,
		EndName:         &endName,
		EndCoordinate:   &endCoordinate,
		Mileage:         &mileage,
		MaxSpeed:        &maxSpeed,
		StartTime:       &startTime,
		EndTime:         &endTime,
		City:            &city,
		List:            locationList,
		AvgSpeed:        &avgSpeed,
		IsFinish:        &isFinish,
	}

	trace_log.DebugLogger(bikeInfo.SNCode, "UpdateCyclingRecord RouteList 开始创建骑行记录 trackId:", trackId, " isEndUpdate:", isEndUpdate)
	// 创建记录
	err := bll.RouteList.Update(context.Background(), record)

	if err != nil {
		trace_log.DebugLogger(bikeInfo.SNCode, "UpdateCyclingRecord RouteList Update err:", err, " record:", utils.ToJsonString(record))
	}

	// 更新骑行记录
	trace_log.DebugLogger(bikeInfo.SNCode, "更新骑行记录 总里程:", endMileage, " 单次里程：", mileage)
	bll.CyclingRecode.UpdateCyclingRecord(userId, bikeId, endMileage, mileage)

	/*	if bikeInfo != nil {
		trace_log.DebugLogger(bikeInfo.SNCode, "----------- UpdateCyclingRecord 定时同步打印轨迹 开始 -------------")
		for i := 0; i < len(results); i++ {
			trace_log.TrackListLogger(bikeInfo.SNCode, utils.ToJsonString(results[i]))
		}
		trace_log.DebugLogger(bikeInfo.SNCode, "----------- UpdateCyclingRecord 定时同步打印轨迹 结束 -------------")
	}*/

	if isEndUpdate {
		d.FinishTrack(bikeInfo.SNCode, userId, bikeId)

		// 记录最后一次车辆位置
		if bikeInfo != nil {
			data, _ := json.Marshal(endCoordinate)
			Consumer.SyncDeviceInfoToShadow(bikeInfo.SNCode, dto.EnUpdateType_ATTRIBUTE, map[string]interface{}{
				"location": data,
			})
		}

		if bikeInfo != nil && bikeInfo.SNCode != "" {
			trace_log.TrackListLogger(bikeInfo.SNCode, "-------------------------------- 骑行记录开始 --------------------------------")
			for i := 0; i < len(results); i++ {
				trace_log.TrackListLogger(bikeInfo.SNCode, utils.ToJsonString(results[i]))
			}
			trace_log.TrackListLogger(bikeInfo.SNCode, "-------------------------------- location 开始 --------------------------------")
			for i := 0; i < len(locationList); i++ {
				trace_log.TrackListLogger(bikeInfo.SNCode, utils.ToJsonString(locationList[i]))
			}
			trace_log.TrackListLogger(bikeInfo.SNCode, "-------------------------------- location 结束 --------------------------------")
			trace_log.TrackListLogger(bikeInfo.SNCode, "-------------------------------- 骑行记录结束 --------------------------------")
		}
	}

	return nil
}

// LocationReport 位置上报
func (d *deviceCyclingReport) LocationReport(data *dto.Location) error {
	// 记录最后一次车辆位置
	point := &po.Point{
		X: float64(data.Lng) / 1000000.0,
		Y: float64(data.Lat) / 1000000.0,
	}

	if point.X == 361 && point.Y == 361 {
		return fmt.Errorf("无效的经纬度")
	}

	_data, _ := json.Marshal(point)

	Consumer.SyncDeviceInfoToShadow(data.Sn, dto.EnUpdateType_ATTRIBUTE, map[string]interface{}{
		"location": string(_data),
	})

	// 获取bikeId
	bikeId, err := bll.CacheBike.GetBikeIdBySn(data.Sn)
	if err != nil {
		log.Errorf("consumer handle LocationReport err:%v", err)
		trace_log.DebugLogger(data.Sn, "LocationReport err:%v", err)
		return err
	}

	// 判断是否已经上电
	if !d.IsPowerOn(bikeId) {
		// 触发偷车报警
		goroutine_pool.GetPool().Push(nil, func(params interface{}) error {
			if _err := AlarmReport.TriggerStealAlarm(data.Sn, data); _err != nil {
				trace_log.DebugLogger(data.Sn, "LocationReport TriggerStealAlarm err:", _err)
			}
			return nil
		})
		return nil
	}

	// 添加key
	if _, ok := d.timerSyncMap.Load(bikeId); !ok {
		d.timerSyncMap.Store(bikeId, struct{}{})
	}

	// 上报数据
	Location.PushLocation(data.Sn, bikeId, data)

	// 重置上电记录
	redis.KV.Expire(context.Background(), redis.CacheValue, fmt.Sprintf("%v:%v", DevicePowerOnStatusKey, bikeId), time.Minute*2)

	// 电子围栏触发
	goroutine_pool.GetPoolV2("ElectronicFenceTrigger").Push(nil, func(params interface{}) error {
		AlarmReport.ElectronicFenceTrigger(bikeId, data.Sn)
		return nil
	})
	return nil
}

// onPolling 定时器
func (d *deviceCyclingReport) onPolling() {
	defer utils.ExceptionCatch()

	timer := time.NewTicker(time.Second * 60)
	for {
		select {
		case <-timer.C:
			d.timerSyncMap.Range(func(key, value interface{}) bool {
				defer utils.ExceptionCatch()
				// 更新骑行记录
				d.UpdateCyclingRecord(key.(int64), false)
				return true
			})
		}
	}

}
